#include "ffmpeg.h"
#include "v4l2.h"
AVCodecContext *global_codec_ctx = nullptr; //H.264编码上下文(编解码)
AVFormatContext *global_fmt_ctx = nullptr;  //格式上下文 mp4容器
struct SwsContext *global_sws_ctx = nullptr; //格式转换上下文
AVFrame *global_frame = nullptr; //视频帧缓冲区
AVPacket *global_pkt = nullptr; //编码后的数据包
int ffmpeg_init(void){

    av_log_set_level(AV_LOG_DEBUG); // 启用 FFmpeg 调试日志

    //search encoder
    const AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264);
    if (!codec) {
        std::cerr << "未找到 H.264 编码器" << std::endl;
        close(fd);
        return -1;
    }

    // 编码上下文
    global_codec_ctx = avcodec_alloc_context3(codec);
    if(!global_codec_ctx){
        std::cerr << "无法分配编码上下文" << std::endl;
        close(fd);
        return -1;
    }
    global_codec_ctx->bit_rate = 1000000; // 提高比特率
    global_codec_ctx->width = WIDTH;
    global_codec_ctx->height = HEIGHT;
    global_codec_ctx->time_base = (AVRational){1, FPS};
    global_codec_ctx->framerate = (AVRational){FPS, 1};
    global_codec_ctx->gop_size = 12;
    global_codec_ctx->max_b_frames = 1;
    global_codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;

    av_opt_set(global_codec_ctx->priv_data, "preset", "ultrafast", 0);
    av_opt_set(global_codec_ctx->priv_data, "tune", "zerolatency", 0);
    if (avcodec_open2(global_codec_ctx, codec, NULL) < 0) {
        std::cerr << "无法打开编码器" << std::endl;
//        cleanup();
        close(fd);
        return -1;
    }

    //格式上下文(创建MP4格式)
    if (avformat_alloc_output_context2(&global_fmt_ctx, NULL, "mp4", OUT_FILENAME) < 0) {
        std::cerr << "无法创建输出上下文" << std::endl;
        close(fd);
//        cleanup();
        return -1;
    }

    // 添加视频流(H264)
    AVStream *stream = avformat_new_stream(global_fmt_ctx, codec);
    if (!stream) {
        std::cerr << "无法创建视频流" << std::endl;
        close(fd);
//        cleanup();
        return -1;
    }

    //写入MP4文件头
    avcodec_parameters_from_context(stream->codecpar, global_codec_ctx);

    // 打开输出文件
    if (avio_open(&global_fmt_ctx->pb, OUT_FILENAME, AVIO_FLAG_WRITE) < 0) {
        std::cerr << "无法打开输出文件: " << strerror(errno) << std::endl;
        close(fd);
//        cleanup();
        return -1;
    }

    // 写入文件头
    if (avformat_write_header(global_fmt_ctx, NULL) < 0) {
        std::cerr << "无法写入文件头" << std::endl;
        close(fd);
//        cleanup();
        return -1;
    }

    // --- 格式转换初始化 ---
    global_sws_ctx = sws_getContext(WIDTH, HEIGHT, AV_PIX_FMT_UYVY422,
                                    WIDTH, HEIGHT, AV_PIX_FMT_YUV420P,
                                    SWS_BILINEAR, NULL, NULL, NULL);
    if (!global_sws_ctx) {
        std::cerr << "无法初始化格式转换上下文" << std::endl;
        close(fd);
//        cleanup();
        return -1;
    }


    // --- 帧分配 ---
    global_frame = av_frame_alloc();
    if (!global_frame) {
        std::cerr << "无法分配帧" << std::endl;
        close(fd);
//        cleanup();
        return -1;
    }
    global_frame->format = global_codec_ctx->pix_fmt;
    global_frame->width = global_codec_ctx->width;
    global_frame->height = global_codec_ctx->height;
    if (av_frame_get_buffer(global_frame, 32) < 0) {
        std::cerr << "无法分配帧缓冲区" << std::endl;
        close(fd);
//        cleanup();
        return -1;
    }

    // --- 数据包分配 ---
    global_pkt = av_packet_alloc();
    if (!global_pkt) {
        std::cerr << "无法分配数据包" << std::endl;
        close(fd);
//        cleanup();
        return -1;
    }

    int frame_count = 0;
    while(frame_count < 100){
        struct v4l2_buffer buf;
        memset(&buf, 0, sizeof(buf));
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.m.planes = planes.data();
        buf.length = planes.size();

        if (ioctl(fd, VIDIOC_DQBUF, &buf) < 0) {
            std::cerr << "从缓冲区取帧失败: " << strerror(errno) << std::endl;
            break;
        }
        std::cout << "获取帧 " << frame_count << "，缓冲区索引: " << buf.index << "，大小: " << buf.m.planes[0].bytesused << std::endl;
        // 验证缓冲区数据大小
        if (buf.m.planes[0].bytesused != WIDTH * HEIGHT * 2) {
            std::cerr << "警告: 缓冲区数据大小异常 (" << buf.m.planes[0].bytesused << " 字节，预期 " << WIDTH * HEIGHT * 2 << " 字节)" << std::endl;
        }


        // 将 UYVY 数据转换为 YUV420P
        uint8_t *src_data[1] = { static_cast<uint8_t*>(global_buffers[buf.index].start) };
        int src_linesize[1] = { WIDTH * 2 };
        sws_scale(global_sws_ctx, src_data, src_linesize, 0, HEIGHT, global_frame->data, global_frame->linesize);

        // 设置帧时间戳
        global_frame->pts = av_rescale_q(frame_count, (AVRational){1, FPS}, global_codec_ctx->time_base);

        // 发送帧到编码器
        if (avcodec_send_frame(global_codec_ctx, global_frame) < 0) {
            std::cerr << "发送帧到编码器失败" << std::endl;
            break;
        }

        // 接收编码后的数据包
        while (1) {
            int ret = avcodec_receive_packet(global_codec_ctx, global_pkt);
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                break;
            if (ret < 0) {
                std::cerr << "编码过程中出错: " << ret << std::endl;
                break;
            }
            global_pkt->stream_index = stream->index;
            av_packet_rescale_ts(global_pkt, global_codec_ctx->time_base, stream->time_base);
            if (av_interleaved_write_frame(global_fmt_ctx, global_pkt) < 0) {  //写入mp4文件
                std::cerr << "写入数据包失败" << std::endl;
            }
            av_packet_unref(global_pkt);
        }
        // 重新将缓冲区加入队列
        if (ioctl(fd, VIDIOC_QBUF, &buf) < 0) {
            std::cerr << "缓冲区重新入队失败: " << strerror(errno) << std::endl;
            break;
        }

        frame_count++;
    }

    avcodec_send_frame(global_codec_ctx, nullptr);
    while(1){
        int ret = avcodec_receive_packet(global_codec_ctx, global_pkt);
        if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
            break;
        if (ret < 0) {
            std::cerr << "刷新编码器失败" << std::endl;
            break;
        }
        global_pkt->stream_index = stream->index;
        av_packet_rescale_ts(global_pkt, global_codec_ctx->time_base, stream->time_base);

//        if (global_fmt_ctx && global_fmt_ctx->streams[0]) {
//            global_pkt->stream_index = global_fmt_ctx->streams[0]->index;
//            av_packet_rescale_ts(global_pkt, global_codec_ctx->time_base, global_fmt_ctx->streams[0]->time_base);
            if (av_interleaved_write_frame(global_fmt_ctx, global_pkt) < 0) {
                std::cerr << "写入刷新数据包失败" << std::endl;
            }
//        }
        av_packet_unref(global_pkt);
    }

    return 1;
}
